package view;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;

import javax.swing.JPanel;

import model.AppointmentDataModel;
import model.CalendarViewMode;
import model.CalendarWeekDays;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Observable;
import java.util.Observer;

/**
 * 06-01-2010
 * 
 * This gives the content of the calender.
 * 
 * @author Dave Nieuwenhuijzen
 */
public class CalendarContentView extends JPanel implements Observer  {

	private static final long serialVersionUID = 1L;
	private model.CalendarModel calendarModel;
	private model.AppointmentModel appointmentModel;

	private int startHour = 8;
	private int endHour = 18;

	private Calendar today = new GregorianCalendar();

	//public UnitsPassing[] passingItems = new UnitsPassing[48];

	//public Vector<model.AppointmentModel> passingItems = new Vector<model.AppointmentModel>();
	/**
	 * This method calls the calendar and appointment model.
	 */
	public CalendarContentView(model.CalendarModel setCalendarModel, model.AppointmentModel setAppointmentModel) {
		calendarModel = setCalendarModel;
		appointmentModel = setAppointmentModel;

		setSize(800, 500);		
	}

	/**
	 * This method is called whenever the observed object is changed.
	 * 
	 * @param observable
	 * @param argument
	 */
	public void update(Observable observable, Object argument) {
		this.revalidate();
		this.repaint();

		resetBounds();
	}

	/**
	 * This method resets all components to its default values and position.
	 */
	public void resetBounds() {
		if(calendarModel.getCalendarViewMode() != model.CalendarViewMode.month) {
			setBounds(0, getHeight(), getWidth() - calendarModel.getScrollbar().getWidth(), calendarModel.getScrollbar().getHeight());
		} else {
			setBounds(0, 25, getWidth(), getHeight() - 25);
		}
	}

	/**
	 * This method paints all components of the calendar content
	 */
	public void paintComponent(Graphics graphics) {
		super.paintComponent(graphics);

		graphics.translate(0, -calendarModel.getScrollValue());

		if(calendarModel.getCalendarViewMode() == CalendarViewMode.week) {
			paintStyleWeek(graphics);
		} else {
			paintStyleMonth(graphics);
		}
	}

	/**
	 * Draw the week view.
	 * 
	 * @param graphics The graphics object of this JPanel
	 */
	private void paintStyleWeek(Graphics graphics) {
		setBackground(Color.WHITE);
		graphics.setColor(new Color(204, 204, 204, 255));

		calendarModel.calculateWeekDayWidth(getWidth());

		for (int i = 1; i <= 7; i++) {
			graphics.drawLine(calendarModel.getLimitsHorizontal(i), 0, calendarModel.getLimitsHorizontal(i), calendarModel.getScrollMax());
		}

		int i = 0;

		Calendar[] weekDays = calendarModel.calculateWeekDays(calendarModel.getSelectedDate());	

		for(Calendar date : weekDays) {
			graphics.setColor(Color.BLACK);

			if(util.CalendarConvert.compareDates(today, date) == true) {
				graphics.setColor(new Color(237, 243, 250, 255));
				graphics.fillRect(calendarModel.getLimitsHorizontal(i) + 1, 0, calendarModel.getLimitsHorizontal(i + 1) - calendarModel.getLimitsHorizontal(i) - 1, calendarModel.getScrollMax());
			}

			i++;
		}

		graphics.setColor(new Color(0, 0, 0, 12));
		graphics.fillRect(0, 0, getWidth(), calendarModel.getPositionHours(startHour, 0));
		graphics.fillRect(0, calendarModel.getPositionHours(endHour, 0),getWidth(), calendarModel.getScrollMax() - calendarModel.getPositionHours(endHour, 0));

		paintHours(graphics);

		drawAppointmentWeek(graphics);
	}

	/**
	 * Paint the hours on the week view.
	 * 
	 * @param graphics The graphics object of this JPanel
	 */
	private void paintHours(Graphics graphics) {
		int height;

		for (int i = 1; i < 48; i++) {
			if ((i % 2) == 0) {
				graphics.setColor(new Color(204, 204, 204, 255));
			} else {
				GregorianCalendar hours = new GregorianCalendar(1, 1, 1, (i / 2), 0, 0);

				SimpleDateFormat dateFormat = new SimpleDateFormat("H:mm");

				graphics.setColor(Color.BLACK);

				graphics.drawString(dateFormat.format(hours.getTime()), 5, ((calendarModel.getScrollMax() * i) / 48));

				graphics.setColor(new Color(229, 229, 229, 255));
			}

			height = (calendarModel.getScrollMax() * i) / 48;
			graphics.drawLine(40, height, getWidth(), height);
		}
	}

	/**
	 * Draw the month view.
	 * 
	 * @param graphics The graphics object of this JPanel
	 */
	private void paintStyleMonth(Graphics graphics) {
		setBackground(Color.WHITE);

		int dayNumbersToDrawAfterCurrentMonth = 49;

		for (int i = 1; i <= 7; i++) {
			calendarModel.setLimitsHorizontal(i, (getWidth() * i) / 7);
			graphics.drawLine(calendarModel.getLimitsHorizontal(i), calendarModel.getLimitsVertical(0), calendarModel.getLimitsHorizontal(i), calendarModel.getScrollMax() + 31);
		}

		for (int i = 0; i <= 6; i++) {
			calendarModel.setLimitsVertical(i, 31 + (((getHeight() - 31) * i) / 6));
			graphics.drawLine(0, calendarModel.getLimitsVertical(i), getWidth(), calendarModel.getLimitsVertical(i));
		}

		CalendarWeekDays[] calendarWeekDays = CalendarWeekDays.values();
		for(int i = 0; i < CalendarWeekDays.values().length; i++) {
			dayNumbersToDrawAfterCurrentMonth--;
			graphics.drawString(calendarWeekDays[i].toString(), (calendarModel.getLimitsHorizontal(i) + calendarModel.getLimitsHorizontal(i + 1)) / 2 - 30, calendarModel.getLimitsVertical(0) - 10);
		}

		int column = 0;
		int row = 0;

		graphics.setColor(Color.BLACK);

		int[] datesFromLastMonth = calendarModel.getLastDaysOfPreviousMonth(calendarModel.getSelectedDate().get(Calendar.YEAR), calendarModel.getSelectedDate().get(Calendar.MONTH));

		int firstDayOfMonth = new GregorianCalendar(calendarModel.getSelectedDate().get(Calendar.YEAR), calendarModel.getSelectedDate().get(Calendar.MONTH), 1).get(Calendar.DAY_OF_WEEK);

		graphics.setColor(Color.GRAY);
		for (int i = 1; i < firstDayOfMonth; i++) {
			graphics.drawString(Integer.toString(datesFromLastMonth[i]), calendarModel.getLimitsHorizontal(column + 1) - 17, calendarModel.getLimitsVertical(row) + 15);
			column++;
			dayNumbersToDrawAfterCurrentMonth--;
		}

		int daysInMonth = calendarModel.getSelectedDate().getActualMaximum(Calendar.DAY_OF_MONTH);

		for(int i = 1; i <= daysInMonth; i++) {
			GregorianCalendar date = new GregorianCalendar(calendarModel.getSelectedDate().get(Calendar.YEAR), calendarModel.getSelectedDate().get(Calendar.MONTH), i);

			if(util.CalendarConvert.compareDates(today, date) == true) {
				graphics.setColor(new Color(237, 243, 250, 255));
				graphics.fillRect(calendarModel.getLimitsHorizontal(column) + 1, calendarModel.getLimitsVertical(row) + 1, calendarModel.getLimitsHorizontal(column + 1) - calendarModel.getLimitsHorizontal(column) - 1, calendarModel.getLimitsVertical(row + 1) - calendarModel.getLimitsVertical(row) - 1);
			}

			graphics.setColor(Color.BLACK);
			graphics.drawString(Integer.toString(i), calendarModel.getLimitsHorizontal(column + 1) - 17, calendarModel.getLimitsVertical(row) + 15);

			column++;
			dayNumbersToDrawAfterCurrentMonth--;
			if (column == 7) {
				column = 0;
				row++;
			}
		}

		graphics.setColor(Color.GRAY);
		for(int i = 1; i <= dayNumbersToDrawAfterCurrentMonth; i++) {

			graphics.drawString(Integer.toString(i), calendarModel.getLimitsHorizontal(column + 1) - 17, calendarModel.getLimitsVertical(row) + 15);
			column++;
			if (column == 7) {
				column = 0;
				row++;
			}
		}

		drawAppointmentMonth(graphics);
	}

	/**
	 * Draw an appointment on the week view.
	 * 
	 * @param graphics The graphics object of this JPanel
	 */
	public void drawAppointmentWeek(Graphics graphics) {
		calendarModel.calculateWeekDayWidth(getWidth());

		int columnWidth = (calendarModel.getLimitsHorizontal(2) - calendarModel.getLimitsHorizontal(1)) - 1;

		Calendar[] weekDays = calendarModel.calculateWeekDays(calendarModel.getSelectedDate());

		calendarModel.setAppointmentData(appointmentModel.getAllAppointments(weekDays[0], weekDays[6]));
		
		for(AppointmentDataModel appointmentData : calendarModel.getAppointmentData()) {
			Calendar endDate = appointmentData.getEndDate();
			if ((endDate.get(Calendar.HOUR) == 0) && (endDate.get(Calendar.MINUTE) == 0)) {
				if (appointmentData.getStartDate() != endDate) {
					endDate.add(Calendar.SECOND, -1);
				} else {
					endDate.add(Calendar.MINUTE, 30);
				}
			}

			int startDatePosition = calendarModel.getPositionHours(appointmentData.getStartDate().get(Calendar.HOUR_OF_DAY), appointmentData.getStartDate().get(Calendar.MINUTE));
			int endDatePosition = calendarModel.getPositionHours(endDate.get(Calendar.HOUR_OF_DAY), endDate.get(Calendar.MINUTE));

			appointmentData.setBounds(new Rectangle(calendarModel.getLimitsHorizontal(appointmentData.getStartDate().get(Calendar.DAY_OF_WEEK) - 1), startDatePosition, calendarModel.getLimitsHorizontal(appointmentData.getStartDate().get(Calendar.DAY_OF_WEEK) - 1) + columnWidth, endDatePosition));

			drawAppointmentWeekTitle(graphics, appointmentData, appointmentData.getStartDate(), endDate, true, true);
		}
	}
	
	/**
	 * Method to paint the title of an appointment in the week title. If the appointment is longer then 30 minutes we paint a header
	 * with the start time in the top left corner.
	 * 
	 * @param graphics The graphics object of the current JPanel.
	 * @param appointmentData The data of the appointment to be painted.
	 * @param startDate The startdate of the appointment, this can differ from the one in appointmentData when the appointment is on multiple days.
	 * @param endDate The enddate of the appointment, this can differ from the one in appointmentData when the appointment is on multiple days.
	 * @param drawBegin If the appointment continues on the next day this will be false.
	 * @param drawEnd If the appointment continues on the next day this will be false for the first day because we want to continue to draw.
	 */
	private void drawAppointmentWeekTitle(Graphics graphics, AppointmentDataModel appointmentData, Calendar startDate, Calendar endDate, boolean drawBegin, boolean drawEnd) {
		int startDatePosition = calendarModel.getPositionHours(startDate.get(Calendar.HOUR_OF_DAY), startDate.get(Calendar.MINUTE));
		int endDatePosition = calendarModel.getPositionHours(endDate.get(Calendar.HOUR_OF_DAY), endDate.get(Calendar.MINUTE));

		if((startDatePosition == 0) && (endDatePosition == 0)) {
			return;
		}

		if(endDatePosition < startDatePosition) {
			endDatePosition = startDatePosition + calendarModel.getPositionHours(0, 30) + 1;
		}

		if(drawBegin == false) {
			startDatePosition -= 15;
		}

		if(drawEnd == false) {
			endDatePosition += 7;
		}

		// Outside borders of appointment
		graphics.setColor(Color.BLUE);

		graphics.drawLine((int)appointmentData.getBounds().getX(), startDatePosition + 6, (int)appointmentData.getBounds().getX(), endDatePosition - 7);
		graphics.drawLine((int)appointmentData.getBounds().getWidth(), startDatePosition + 6, (int)appointmentData.getBounds().getWidth(), endDatePosition - 7);
		graphics.drawLine((int)appointmentData.getBounds().getX() + 6, startDatePosition, (int)appointmentData.getBounds().getWidth() - 6, startDatePosition);
		graphics.drawLine((int)appointmentData.getBounds().getX() + 6, endDatePosition - 1, (int)appointmentData.getBounds().getWidth() - 6, endDatePosition - 1);

		// Corner top left
		graphics.drawLine((int)appointmentData.getBounds().getX() + 4, startDatePosition + 1, (int)appointmentData.getBounds().getX() + 5, startDatePosition + 1);
		graphics.drawLine((int)appointmentData.getBounds().getX() + 3, startDatePosition + 2, (int)appointmentData.getBounds().getX() + 2, startDatePosition + 3);
		graphics.drawLine((int)appointmentData.getBounds().getX() + 1, startDatePosition + 4, (int)appointmentData.getBounds().getX() + 1, startDatePosition + 5);

		// Corner top right
		graphics.drawLine((int)appointmentData.getBounds().getWidth() - 5, startDatePosition + 1, (int)appointmentData.getBounds().getWidth() - 4, startDatePosition + 1);
		graphics.drawLine((int)appointmentData.getBounds().getWidth() - 3, startDatePosition + 2, (int)appointmentData.getBounds().getWidth() - 2, startDatePosition + 3);
		graphics.drawLine((int)appointmentData.getBounds().getWidth() - 1, startDatePosition + 4, (int)appointmentData.getBounds().getWidth() - 1, startDatePosition + 5);

		// Corner bottom right
		graphics.drawLine((int)appointmentData.getBounds().getWidth() - 5, endDatePosition - 2, (int)appointmentData.getBounds().getWidth() - 4, endDatePosition - 2);
		graphics.drawLine((int)appointmentData.getBounds().getWidth() - 2, endDatePosition - 4, (int)appointmentData.getBounds().getWidth() - 3, endDatePosition - 3);
		graphics.drawLine((int)appointmentData.getBounds().getWidth() - 1, endDatePosition - 6 , (int)appointmentData.getBounds().getWidth() - 1, endDatePosition - 5);

		// Corner bottom left
		graphics.drawLine((int)appointmentData.getBounds().getX()+4, endDatePosition-2, (int)appointmentData.getBounds().getX()+5, endDatePosition-2);
		graphics.drawLine((int)appointmentData.getBounds().getX()+3, endDatePosition-3, (int)appointmentData.getBounds().getX()+2, endDatePosition-4);
		graphics.drawLine((int)appointmentData.getBounds().getX()+1, endDatePosition-6, (int)appointmentData.getBounds().getX()+1, endDatePosition-5);

		// If the difference between startDatePosition and endDatePosition is greater then 30 we paint a title with the start time of appointment.
		if((endDatePosition - startDatePosition - 1) > 30) {
			graphics.drawLine((int)appointmentData.getBounds().getX() + 6, startDatePosition + 1, (int)appointmentData.getBounds().getWidth() - 6, startDatePosition + 1);
			graphics.drawLine((int)appointmentData.getBounds().getX() + 4, startDatePosition + 2, (int)appointmentData.getBounds().getWidth() - 4, startDatePosition + 2);
			graphics.drawLine((int)appointmentData.getBounds().getX() + 3, startDatePosition + 3, (int)appointmentData.getBounds().getWidth() - 3, startDatePosition + 3);
			graphics.drawLine((int)appointmentData.getBounds().getX() + 2, startDatePosition + 4, (int)appointmentData.getBounds().getWidth() - 2, startDatePosition + 4);
			graphics.drawLine((int)appointmentData.getBounds().getX() + 2, startDatePosition + 5, (int)appointmentData.getBounds().getWidth() - 2, startDatePosition + 5);

			graphics.fillRect((int)appointmentData.getBounds().getX() + 1, startDatePosition + 6, (int)appointmentData.getBounds().getWidth() - (int)appointmentData.getBounds().getX() - 1, 9);

			graphics.setColor(new Color(245, 118, 0, 255));

			Font font = new Font("Dialog", Font.BOLD, 10);
			graphics.setFont(font);

			SimpleDateFormat timeFormat = new SimpleDateFormat("H:mm");
			graphics.drawString(timeFormat.format(appointmentData.getStartDate().getTime()), (int)appointmentData.getBounds().getX() + 8, startDatePosition + 10);

			graphics.drawString(appointmentData.getTitle(), (int)appointmentData.getBounds().getX() + 5, startDatePosition + 25);
		} else {
			graphics.setColor(new Color(245, 118, 0, 255));

			Font font = new Font("Dialog", Font.BOLD, 10);
			graphics.setFont(font);

			graphics.drawString(appointmentData.getTitle(), (int)appointmentData.getBounds().getX() + 8, startDatePosition + 9);

			SimpleDateFormat timeFormat = new SimpleDateFormat("H:mm");
			graphics.drawString(timeFormat.format(appointmentData.getStartDate().getTime()), (int)appointmentData.getBounds().getWidth() - 25, startDatePosition + 9);
		}
	}
	
	/**
	 * Draw appointments on the month view
	 * 
	 * @param graphics The graphics object of this JPanel object
	 */
	public void drawAppointmentMonth(Graphics graphics) {
		int column = 0;
		int row = 0;

		Calendar week = (Calendar) calendarModel.getSelectedDate().clone();
		week.set(week.get(Calendar.YEAR), week.get(Calendar.MONTH), 1);

		for(int j = 0; j < 6; j++) {
			Calendar[] weekDays = calendarModel.calculateWeekDays(week);
			week.add(Calendar.DAY_OF_MONTH, 7);
			
			for(int i = 0; i < 7; i++) {
				int line = 0;

				// Loop through all appointments
				for(model.AppointmentDataModel appointment : appointmentModel.getAllAppointments()) {
					if (weekDays[i].get(Calendar.MONTH) == calendarModel.getSelectedDate().get(Calendar.MONTH) && util.CalendarConvert.compareDates(weekDays[i], appointment.getStartDate()) == true) {

						row = (appointment.getStartDate().get(Calendar.DAY_OF_MONTH) + 6) / 7;

						int yPosition = calendarModel.getLimitsVertical(row) + 5 + (line * 13) + 11;
						if (yPosition < calendarModel.getLimitsVertical(row + 1) - 11) {
							Font font = new Font("Dialog", Font.BOLD, 10);
							graphics.setFont(font);

							SimpleDateFormat timeFormat = new SimpleDateFormat("H:mm");
							graphics.drawString(timeFormat.format(appointment.getStartDate().getTime()) + " " + appointment.getTitle(), calendarModel.getLimitsHorizontal(column) + 5, yPosition);
						}

						line++;
					}
				}

				column++;

				// If column is equal to 7 we reset it
				if (column == 7) {
					column = 0;
					row++;
				}
			}
		}
	}
}